package com.kingwang.demo.springsecuritytokendemo.security;

import com.kingwang.demo.springsecuritytokendemo.config.SessionConfig;
import com.kingwang.demo.springsecuritytokendemo.utils.DateTimeGenerator;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;

import java.time.Duration;

/**
 * 抽象TokenSessionManager，用于存放公共方法逻辑
 */
public abstract class AbstractTokenSessionManager implements TokenSessionManager {
    protected SessionConfig sessionConfig;

    protected DateTimeGenerator dateTimeGenerator;

    public AbstractTokenSessionManager(SessionConfig sessionConfig, DateTimeGenerator dateTimeGenerator) {
        this.sessionConfig = sessionConfig;
        this.dateTimeGenerator = dateTimeGenerator;
    }

    /**
     * 判断Session是否过期
     *
     * @param session
     * @return
     */
    @Override
    public boolean isExpired(TokenSession session) {
        // Session活动时间
        long activeSeconds =
                Duration.between(this.dateTimeGenerator.now(), session.getLastActiveTime()).getSeconds();

        return activeSeconds > this.sessionConfig.getExpireDurationSeconds();
    }

    /**
     * 生成Session ID
     *
     * @param type
     * @param token
     * @return
     */
    @Override
    public String generateSessionId(String type, String token) {
        Assert.notNull(type, "Token类型不能为空");
        Assert.notNull(token, "Token不能为空");

        return type.trim() + ":" + token.trim();
    }

    /**
     * 创建Session
     *
     * @param tokenType
     * @param authentication
     * @param userDetails
     * @return
     */
    @Override
    public TokenSession createSession(String tokenType, Authentication authentication, UserDetails userDetails) {
        String token = getTokenByAuthentication(authentication);

        if (token == null) {
            try {
                token = generateTokenByUserDetails(userDetails);
            } catch (Exception e) {
                throw new IllegalStateException("无法生成Token", e);
            }
        }

        return doCreateSession(tokenType, token, userDetails);
    }

    /**
     * 创建Session
     *
     * @param tokenType
     * @param userDetails
     * @return
     */
    @Override
    public TokenSession createSession(String tokenType, UserDetails userDetails) {
        String token;

        try {
            token = generateTokenByUserDetails(userDetails);
        } catch (Exception e) {
            throw new IllegalStateException("无法生成Token", e);
        }

        return doCreateSession(tokenType, token, userDetails);
    }

    /**
     * 基于Spring Security的Authentication生成Token
     *
     * @param authentication
     * @return
     */
    private String getTokenByAuthentication(Authentication authentication) {
        if (isTokenBasedAuthenticationToken(authentication)) {
            return ((TokenBasedAuthenticationToken) authentication).getToken();
        }

        return null;
    }

    /**
     * 通过UserDetails生成Token
     *
     * @param userDetails
     * @return
     * @throws Exception
     */
    protected abstract String generateTokenByUserDetails(UserDetails userDetails) throws Exception;

    /**
     * 通过Token和UserDetails创建Session
     *
     * @param tokenType
     * @param token
     * @param userDetails
     * @return
     */
    protected abstract TokenSession doCreateSession(String tokenType, String token, UserDetails userDetails);

    /**
     * 删除Session
     *
     * @param session
     */
    @Override
    public void removeSession(TokenSession session) {
        this.removeSession(session.getId());
    }

    protected static boolean isTokenBasedAuthenticationToken(Authentication authentication) {
        return authentication instanceof TokenBasedAuthenticationToken;
    }
}
